/*
 *  CSE 40166, Computer Graphics, Fall 2013
 *  Assignment 4, Part 2
 *
 *  File: Object.cpp
 *
 *  Kevin Sonnen
 *
 *  Description: Contains implementation of classes used to store an object
 *
 */

#include "Object.h"

////////////////////////////////////////////
// Face Member Functions
////////////////////////////////////////////

//
// face()
//
// Constructor for a face object. Initialize vertex, texture, normal arrays
//
face::face() {
	vertexIndices = new unsigned int[3];
	textureIndices = new unsigned int[3];
	normalIndices = new unsigned int[3];
	hasTexture = false;
	hasNormals = false;
}

//
// void setVertexIndices(const unsigned int &v1, const unsigned int &v2, const unsigned int &v3)
//
// Set the vertex indices to the integers passed in
//
void 
face::setVertexIndices(const unsigned int &v1, const unsigned int &v2, const unsigned int &v3) {
	vertexIndices[0] = v1;
	vertexIndices[1] = v2;
	vertexIndices[2] = v3;
}

//
// void setTextureIndices(const unsigned int &t1, const unsigned int &t2, const unsigned int &t3)
//
// Set the texture indices to the integers passed in
//
void 
face::setTextureIndices(const unsigned int &t1, const unsigned int &t2, const unsigned int &t3) {
	textureIndices[0] = t1;
	textureIndices[1] = t2;
	textureIndices[2] = t3;
	hasTexture = true;
}

//
// void setNormalIndices(const unsigned int &n1, const unsigned int &n2, const unsigned int &n3)
//
// Set the normal indices to the integers passed in
//
void 
face::setNormalIndices(const unsigned int &n1, const unsigned int &n2, const unsigned int &n3) {
	normalIndices[0] = n1;
	normalIndices[1] = n2;
	normalIndices[2] = n3;
	hasNormals = true;
}

//
// unsigned int * getVertexIndices()
//
// Return the vertex indices for the face
//
unsigned int *
face::getVertexIndices() {
	return vertexIndices;
}

//
// unsigned int * getVertexIndices()
//
// Return the texture indices for the face
//
unsigned int *
face::getTextureIndices() {
	return textureIndices;
}

//
// unsigned int * getVertexIndices()
//
// Return the normal indices for the face
//
unsigned int *
face::getNormalIndices() {
	return normalIndices;
}

//
// bool mapTexture()
//
// Return whether or not the face has an associated texture coordinate
//
bool 
face::mapTexture() {
	return hasTexture;
}

//
// bool mapTexture()
//
// Return whether or not the face has an associated normal vector
//
bool 
face::mapNormals() {
	//std::cout << "HasNormals is " << hasNormals << std::endl;
	return hasNormals;
}

////////////////////////////////////////////
// Object Member Functions
////////////////////////////////////////////

//
// void addVertices(const point & mVerts)
//
// Add a set of 3 vertices to the object's vertices vector
// Check the vertices against our bounding box min/max
// 
void
Object::addVertices(const point & mVerts) {
	vertices.push_back(mVerts);

	// Check the vertex locations against the minimums/maximums to get a bounding box
	static bool firstInstance = true;
	if (firstInstance) {
		firstInstance = false;
		boundingBox.minX = mVerts.coord[0];
		boundingBox.minY = mVerts.coord[1];
		boundingBox.minZ = mVerts.coord[2];
		boundingBox.maxX = mVerts.coord[0];
		boundingBox.maxY = mVerts.coord[1];
		boundingBox.maxZ = mVerts.coord[2];
	} else {
		if (mVerts.coord[0] < boundingBox.minX)
			boundingBox.minX = mVerts.coord[0];
		if (mVerts.coord[0] > boundingBox.maxX)
			boundingBox.maxX = mVerts.coord[0];
		if (mVerts.coord[1] < boundingBox.minY)
			boundingBox.minY = mVerts.coord[1];
		if (mVerts.coord[1] > boundingBox.maxY)
			boundingBox.maxY = mVerts.coord[1];
		if (mVerts.coord[2] < boundingBox.minZ)
			boundingBox.minZ = mVerts.coord[2];
		if (mVerts.coord[2] > boundingBox.maxZ)
			boundingBox.maxZ = mVerts.coord[2];
	}
}

//
// void setDiameter()
//
// Sets the diameter member variable based on the distance from the
// center of the bouding box to the corner. This can be called after
// reading in all of the vertices of an object, and only needs to be
// called once.
//
void
Object::setDiameter() {
	float X = boundingBox.maxX - boundingBox.minX;
	float Y = boundingBox.maxY - boundingBox.minY;
	float Z = boundingBox.maxZ - boundingBox.minZ;
	float diam = sqrtf(powf(X, 2) + powf(Y, 2) + powf(Z, 2));
	diameter = diam;
}

//
// GLfloat * getVertexLocation(const int &faceNum, const int &vertNum)
//
// Returns an array of the actual coordinates for a given vertex in a
// given face
//
GLfloat *
Object::getVertexLocation(const int &faceNum, const int &vertNum) {
	return vertices[faces[faceNum].getVertexIndices()[vertNum]].coord;
}

//
// GLfloat * getVertexNormals(const int &faceNum, const int &vertNum)
//
// Returns an array representing the normal vector for a given vertex in a
// given face
//
GLfloat *
Object::getVertexNormals(const int &faceNum, const int &vertNum) {
	return normals[faces[faceNum].getNormalIndices()[vertNum]].coord;
}

//
// GLfloat * getVertexTextureCoords(const int &faceNum, const int &vertNum)
//
// Returns an array representing the texture coordinates for a given vertex in a
// given face
//
GLfloat *
Object::getVertexTextureCoords(const int &faceNum, const int &vertNum) {
	return textures[faces[faceNum].getTextureIndices()[vertNum]].coord;
}

//
// Calculate the area of the face with the given index
// This is done by calculating the magnitude of the cross product and dividing by 2
//
// float faceArea(const face &mFace)
//
float 
Object::faceArea(const unsigned int &index) {
	unsigned int *verts = faces[index].getVertexIndices();
	float vec1[3];
	vec1[0] = vertices[verts[1]].coord[0] - vertices[verts[0]].coord[0];
	vec1[1] = vertices[verts[1]].coord[1] - vertices[verts[0]].coord[1];
	vec1[2] = vertices[verts[1]].coord[2] - vertices[verts[0]].coord[2];
	
	float vec2[3];
	vec2[0] = vertices[verts[2]].coord[0] - vertices[verts[0]].coord[0];
	vec2[1] = vertices[verts[2]].coord[1] - vertices[verts[0]].coord[1];
	vec2[2] = vertices[verts[2]].coord[2] - vertices[verts[0]].coord[2];
	
	float cross[3];
	cross[0] = vec1[1]*vec2[2] - vec1[2]*vec2[1];
	cross[1] = vec1[2]*vec2[0] - vec1[0]*vec2[2];
	cross[2] = vec1[0]*vec2[1] - vec1[1]*vec2[0];
	
	return sqrtf(cross[0]*cross[0] + cross[1]*cross[1] + cross[2]*cross[2])/2;
}
